package com.sise.recomder;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sise.entity.ProductCollect;
import com.sise.entity.ProductInfo;
import com.sise.entity.User;
import com.sise.mapper.ProductCollectMapper;
import com.sise.mapper.ProductInfoMapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.TreeSet;

/**
 * @author zj
 */
@Service
public class CollectRecomder {

    @Resource
    private PublicUse publicUse;

    @Resource
    private ProductInfoMapper productInfoMapper;

    @Resource
    private ProductCollectMapper productCollectMapper;

    //通过计算余弦相似度并取TopN, 返回为uid的用户生成的5个推荐菜品
    public List<ProductInfo> recommend(String open_id) {
        //其他用户收藏的菜品列表
        List<ProductCollect> collectArrayList;

        List<User> userList = publicUse.getUserList();
        List<ProductInfo> productInfoList = publicUse.getProductInfoList();
        // 当前矩阵
        int[][] curMatrix = new int[productInfoList.size() + 5][productInfoList.size() + 5];
        // 共现矩阵
        int[][] comMatrix = new int[productInfoList.size() + 5][productInfoList.size() + 5];
        // 喜欢每个物品的人数
        int[] N = new int[productInfoList.size() + 5];

        for (User user : userList) {
            // 当前用户则跳过
            if (user.getOpenId().equals(open_id)) continue;

            QueryWrapper<ProductCollect> productCollectQueryWrapper = new QueryWrapper<>();
            productCollectQueryWrapper.lambda().eq(ProductCollect::getOpenId, user.getOpenId());
            // 当前用户的喜欢列表
            collectArrayList = productCollectMapper.selectList(productCollectQueryWrapper);

            for (int i = 0; i < productInfoList.size(); i++)
                for (int j = 0; j < productInfoList.size(); j++)
                    // 清空矩阵
                    curMatrix[i][j] = 0;

            for (int i = 0; i < collectArrayList.size(); i++) {
                int pid1 = collectArrayList.get(i).getProductId();
                ++N[pid1];
                for (int j = i + 1; j < collectArrayList.size(); j++) {
                    int pid2 = collectArrayList.get(j).getProductId();
                    // 两两加一
                    ++curMatrix[pid1][pid2];
                    ++curMatrix[pid2][pid1];
                }
            }

            // 累加所有矩阵, 得到共现矩阵
            for (int i = 0; i < productInfoList.size(); i++) {
                for (ProductInfo productInfo : productInfoList) {
                    int pid1 = productInfoList.get(i).getProductId(), pid2 = productInfo.getProductId();
                    comMatrix[pid1][pid2] += curMatrix[pid1][pid2];
                    comMatrix[pid1][pid2] += curMatrix[pid1][pid2];
                }
            }
        }

        TreeSet<ProductInfo> preList = publicUse.preprocessingList();

        QueryWrapper<ProductCollect> productCollectQueryWrapper = new QueryWrapper<>();
        productCollectQueryWrapper.lambda().eq(ProductCollect::getOpenId, open_id);
        // 当前用户喜欢的菜品列表
        collectArrayList = productCollectMapper.selectList(productCollectQueryWrapper);
        // 判重数组
        boolean[] used = new boolean[productInfoList.size() + 5];
        for (ProductCollect productCollect : collectArrayList) {
            // 既喜欢i又喜欢j的人数
            int Nij;
            // 相似度
            double Wij;
            // 当前的菜品
            ProductInfo tmp;

            int i = productCollect.getProductId();
            for (ProductInfo productInfo : productInfoList) {
                if (productCollect.getProductId().equals(productInfo.getProductId())) continue;
                int j = productInfo.getProductId();

                Nij = comMatrix[i][j];
                Wij = (double) Nij / Math.sqrt(N[i] * N[j]); //计算余弦相似度

                tmp = productInfoMapper.selectById(productInfo.getProductId());
                tmp.setSimilarity(Wij);

                if (used[tmp.getProductId()]) continue;
                preList.add(tmp);
                used[tmp.getProductId()] = true;
            }
        }
        return publicUse.generateRecommendationResults(preList);
    }
}
